/**
 * @file	adc.c
 * @author	chipsea
 * @brief	Contains all functions support for adc driver
 * @version	0.1
 * @date	2020-11-30
 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd.
 * @note
 */
#include <string.h>
#include "error.h"
#include "gpio.h"
#include "pwrmgr.h"
#include "clock.h"
#include "adc.h"
#include "log.h"
#include "jump_function.h"
// #include "halPeripheral.h"


typedef struct _adc_Contex_t
{
    bool        enable;
    uint8_t     all_channel;
	uint8_t     chs_en_shadow;
    bool        continue_mode;
	
	//sysclk_t    clk_src;
	uint16_t    adc_cal_postive;
    uint16_t    adc_cal_negtive;
	
    adc_Hdl_t   evt_handler;
} adc_Ctx_t;

static adc_Ctx_t mAdc_Ctx = 
{
	.enable = FALSE,
	.all_channel = 0x00,
	.chs_en_shadow = 0x00,
	.continue_mode = FALSE,
	.adc_cal_postive = 0xFFF,
	.adc_cal_negtive = 0xFFF,
	.evt_handler = NULL
};

#define ADC_SAMPING_NUM		64
// #define ADC_MAX_OFFSET		64

#define FILTER_SIZE   4
uint16_t filter(uint32_t base,int ch,int n) 

{ 

	char count,j;
	int offset;
	uint16_t	max ,min ;

	uint16_t value_buf[FILTER_SIZE]; 

	int  sum=0; 
	offset = n;
	for  (count=0;count<FILTER_SIZE;count++) 
	{ 
		value_buf[count ++]= (uint16_t)(read_reg(ADC_CH_BASE + (ch * 0x80) + ((base + offset)<<2))&0xfff);
		value_buf[count] = (uint16_t)((read_reg(ADC_CH_BASE + (ch * 0x80) + ((base + offset)<<2))>>16)&0xfff);
		offset ++;
	} 
	max = value_buf[0];
	min = value_buf[0];
	sum = value_buf[0];
	for(j=1;j<FILTER_SIZE;j++)
	{
		max = (max > value_buf[j])?  max : value_buf[j];
		min = (min > value_buf[j])? value_buf[j] : min ;
		sum += value_buf[j]; 
	}	

	sum -= (max + min);
	return (uint16_t)(sum>>1); 

} 
/**
 * @fn      HalAdcLoadCalibrationValue
 * @brief   Read calibration values from Flash
 * @param   none
 * @return  none
 */
static void HalAdcLoadCalibrationValue(void)
{
    uint32_t adc_cal = read_reg(SPIF_RSVD1_ADC_CALIBRATE);
  mAdc_Ctx.adc_cal_negtive = (uint16_t)(adc_cal&0x0fff);
  mAdc_Ctx.adc_cal_postive = (uint16_t)((adc_cal>>16)&0x0fff);
  LOG("AD_CAL[%x %x]\n",mAdc_Ctx.adc_cal_negtive,mAdc_Ctx.adc_cal_postive);

  if((mAdc_Ctx.adc_cal_negtive < 0x733) || (mAdc_Ctx.adc_cal_negtive > 0x8cc) ||
            (mAdc_Ctx.adc_cal_postive < 0x733) || (mAdc_Ctx.adc_cal_postive > 0x8cc))
  {
    mAdc_Ctx.adc_cal_negtive = 0xfff;
    mAdc_Ctx.adc_cal_postive = 0xfff;
    LOG("->AD_CAL[%x %x]\n",mAdc_Ctx.adc_cal_negtive, mAdc_Ctx.adc_cal_postive);
  }
}


GpioPin_t s_pinmap[ADC_CH_NUM] = {
    GPIO_DUMMY, //ADC_CH0 =0,
    GPIO_DUMMY, //ADC_CH1 =1,
	P11, //ADC_CH1N =2,
	P23, //ADC_CH1P =3,  
	P24, //ADC_CH2N =4,
	P14, //ADC_CH2P =5,  
	P15, //ADC_CH3N =6,
	P20, //ADC_CH3P =7, 
	GPIO_DUMMY,  //ADC_CH_VOICE =8,
};

/**
 * @fn      SetSamplingResolution
 * @brief   Set the sampling resolution
 * @param   channel
 * @param	is_high_resolution
 * @param	is_differential_mode
 * @return  none
 */
static void SetSamplingResolution(AdcChannel_t channel, bool is_high_resolution)
{
	uint8_t aio = 0;
	switch(channel)
	{                
		case ADC_CH1N_P11:
			aio = 0;	
			break;
		case ADC_CH1P_P23:
			aio = 1;
			break;
		case ADC_CH2N_P24:
			aio = 2;
			break;
		case ADC_CH2P_P14:
			aio = 3;
			break;
		case ADC_CH3N_P15:
			aio = 4;
			break;
		case ADC_CH3P_P20:
			aio = 7;
			break;			
		default:
			return;
	}                
	if(is_high_resolution)
	{		
		subWriteReg(&(AP_AON->PMCTL2_1),(aio+8),(aio+8),0);
		subWriteReg(&(AP_AON->PMCTL2_1),aio,aio,1);		    
	}
	else
	{
		subWriteReg(&(AP_AON->PMCTL2_1),(aio+8),(aio+8),1);
		subWriteReg(&(AP_AON->PMCTL2_1),aio,aio,0);		  
	}
}

/**
 * @fn      SetSamplingResolutionAuto
 * @brief   Set the automatic sampling resolution
 * @param   channel
 * @param	is_high_resolution
 * @param	is_differential_mode
 * @return  none
 */
static void SetSamplingResolutionAuto(uint8_t channel, uint8_t is_high_resolution)
{
    uint8_t i_channel;
    AdcChannel_t a_channel;
			
    AP_AON->PMCTL2_1 = 0x00;
    for(i_channel =MIN_ADC_CH;i_channel<=MAX_ADC_CH;i_channel++)
    {
        if(channel & BIT(i_channel))
        {
            a_channel = (AdcChannel_t)i_channel;
            SetSamplingResolution(a_channel,
							    (is_high_resolution & BIT(i_channel)));
        }
    }
}


/**
 * @fn      DisableAnalogPin
 * @brief   disable analog pin
 * @param   channel
 * @return  none
 */
static void DisableAnalogPin(AdcChannel_t channel)
{
    int index = (int)channel;
    GpioPin_t pin = s_pinmap[index];
    if(pin == GPIO_DUMMY)
        return;

    HalGpioAnalogConfig(pin,Bit_DISABLE);
    HalGpioPinInit(pin,GPIO_INPUT);       //ie=0,oen=1 set to imput
    HalGpioPupdConfig(pin,GPIO_FLOATING);    //
}

/**
 * @fn      ClearAdcConfig
 * @brief   Clear configuration
 * @param   none
 * @return  none
 */
static void ClearAdcConfig(void)
{
    mAdc_Ctx.all_channel = 0x00;
	mAdc_Ctx.chs_en_shadow = 0x00;
	mAdc_Ctx.continue_mode = FALSE;
	mAdc_Ctx.evt_handler = NULL;
}

/**
 * @fn      HalAdcIRQHandler
 * @brief   This function process for adc interrupt
 * @param   none
 * @return  none
 */
volatile int flag = 0;
volatile uint32_t adc_pointer;	
 void __attribute__((used)) HalAdcIRQHandler(void)
{
	int ch,status,ch2,n;
	uint16_t *adc_data;
	// uint8_t filter_count =0;

	// hal_clk_gate_disable(MOD_ADCC);
	// AP_PCRM->CLKHF_CTL1_f.DCLK32M_EN = 0;
	status = GET_IRQ_STATUS;
	for (ch = MIN_ADC_CH; ch <= MAX_ADC_CH; ch++)
	{
		if((mAdc_Ctx.all_channel & BIT(ch)) &&(status & BIT(ch)))
		{
			ch2=(ch%2)?(ch-1):(ch+1);
			if(mAdc_Ctx.continue_mode == FALSE)
			{
				AP_ADCC->INT_MASK &= ~ BIT(ch); //MASK coresponding channel
				mAdc_Ctx.all_channel &= ~BIT(ch);//disable channel				
			}
			if (status & BIT(ch)) 
			{
				AP_ADCC->INT_MASK &= ~BIT(ch);
				switch(ch)
				{
					case ADC_CH0:
							adc_pointer = ADC_CH1_POINTER;
					break;
					case ADC_CH1:
							adc_pointer = ADC_CH2_POINTER;
						break;
					case ADC_CH2:
							adc_pointer = ADC_CH3_POINTER;
						break;
					case ADC_CH3:
							adc_pointer = ADC_CH4_POINTER;
						break; 
					case ADC_CH4:
							adc_pointer = ADC_CH5_POINTER;
						break;
					case ADC_CH9:
							adc_pointer = ADC_CH6_POINTER;
							
						break;
					default:
						break;  
				}
				// if(adc_pointer <= 0 || adc_pointer > ADC_MAX_OFFSET)
				// {
				// 	adc_pointer = ADC_SAMPING_NUM ;
				// }
				// else if(adc_pointer < ADC_SAMPING_NUM)
				// {
				// 	adc_pointer += ADC_MAX_OFFSET;
				// 	adc_pointer -= ADC_SAMPING_NUM;
					
				// }else{
				// 	adc_pointer -= ADC_SAMPING_NUM;
				// }
				// adc_pointer = adc_pointer >>1;
				// filter_count =0;
				// for (n = 0; n < (MAX_ADC_SAMPLE_SIZE>>2); n++) 
				// {		
				// 	adc_data[n] = filter(adc_pointer,ch,filter_count);
				// 	filter_count +=2;
				// 	if((adc_pointer + filter_count) > ADC_MAX_OFFSET )
				// 	{
				// 		adc_pointer =0;
				// 		filter_count =0;
				// 	}

				// }			
				adc_data = osal_mem_alloc(ADC_SAMPING_NUM * sizeof(uint16_t));			
				for(int i=0;i<ADC_SAMPING_NUM;i++)
				{
					if(adc_pointer > 0)adc_pointer--;
					else adc_pointer = 63;
					adc_data[i] = AP_ADCC->MEM_CH[ch][adc_pointer];
				}
				AP_ADCC->INT_CLR = BIT(ch);								
				if(mAdc_Ctx.enable == FALSE)
					continue;		

				if (mAdc_Ctx.evt_handler){
					AdcEvt_t evt;
					evt.type = HAL_ADC_EVT_DATA;
					evt.ch = (AdcChannel_t)ch2;
					evt.data = adc_data;
					evt.size = ADC_SAMPING_NUM;
					mAdc_Ctx.evt_handler(&evt);
				}
				osal_mem_free(adc_data);
									
					AP_ADCC->INT_MASK |= BIT(ch);
			}
		}

	}
	// AP_PCRM->CLKHF_CTL1_f.DCLK32M_EN = 1;	
	if((mAdc_Ctx.all_channel == 0) && (mAdc_Ctx.continue_mode == FALSE))//
	{
		HalAdcStop();
	}
}

/**
 * @fn      AdcWakeupHdl
 * @brief   Set the ADC interrupt priority
 * @param   none
 * @return  none
 */
static void AdcWakeupHdl(void)
{
    NVIC_SetPriority((IRQn_Type)ADCC_IRQn, IRQ_PRIO_HAL);
}

/**
 * @fn      HalAdcInit
 * @brief   This function process for adc initial
 * @param   none
 * @return  none
 * @note	mode: adc sample mode select;1:SAM_MANNUAL(mannual mode),0:SAM_AUTO(auto mode)
 *          	  ADC_CH_e adc_pin: adc pin select;ADC_CH0~ADC_CH7 and ADC_CH_VOICE
 *                ADC_SEMODE_e semode: signle-ended mode negative side enable; 1:SINGLE_END(single-ended mode) 0:DIFF(Differentail mode)
 *                IO_CONTROL_e amplitude: input signal amplitude, 0:BELOW_1V,1:UP_1V
 */
void HalAdcInit(void) {
	hal_pwrmgr_register(MOD_ADCC,NULL,AdcWakeupHdl);
    ClearAdcConfig();
	HalAdcLoadCalibrationValue();   
	mAdc_Ctx.enable = TRUE;
}

/**
 * @fn      HalAdcClockConfig
 * @brief   This function process for adc clock
 * @param   clk - clock
 * @return  none
 */
int HalAdcClockConfig(AdcClockSel_t clk){
    if(mAdc_Ctx.enable == FALSE){
        return ERR_NOT_REGISTED;
    }
	if(clk > HAL_ADC_CLOCK_320K)
	{
		return ERR_INVALID_PARAM;
	}
	subWriteReg(0x4000F000 + 0x7c,2,1,clk);
	return SUCCESS;
}

/**
 * @fn      HalAdcStart
 * @brief   start
 * @param   none
 * @return  none
 */
int HalAdcStart(void)
{
	uint8_t     all_channel2 = (((mAdc_Ctx.chs_en_shadow&0x80)>>1)|\
								((mAdc_Ctx.chs_en_shadow&0x40)<<1)|\
								((mAdc_Ctx.chs_en_shadow&0x20)>>1)|\
								((mAdc_Ctx.chs_en_shadow&0x10)<<1)|\
								((mAdc_Ctx.chs_en_shadow&0x08)>>1)|\
								((mAdc_Ctx.chs_en_shadow&0x04)<<1));
	if(mAdc_Ctx.enable == FALSE){
		return ERR_NOT_REGISTED;
	}

	hal_pwrmgr_lock(MOD_ADCC);
	
	JUMP_FUNCTION(ADCC_IRQ_HANDLER)                  =   (uint32_t)&HalAdcIRQHandler;
	//ENABLE_ADC;
	for(int i=MIN_ADC_CH; i<=MAX_ADC_CH; i++)
	{
		if(all_channel2 & (BIT(i)))
		{
			switch (i)
			{
			case ADC_CH1N_P11:
				AP_PCRM->ADC_CTL1 |= BIT(20);
				break;

			case ADC_CH1P_P23:
				AP_PCRM->ADC_CTL1 |= BIT(4);
				break;

			case ADC_CH2N_P24:
				AP_PCRM->ADC_CTL2 |= BIT(20);
				break;

			case ADC_CH2P_P14:
				AP_PCRM->ADC_CTL2 |= BIT(4);
				break;

			case ADC_CH3N_P15:
				AP_PCRM->ADC_CTL3 |= BIT(20);
				break;

			case ADC_CH3P_P20:
				AP_PCRM->ADC_CTL3 |= BIT(4);
				break;
			}
		}		
	}
	AP_PCRM->ANA_CTL |= BIT(3);
	AP_PCRM->ANA_CTL |= BIT(0);//new

	//ADC_IRQ_ENABLE;
	NVIC_EnableIRQ((IRQn_Type)ADCC_IRQn);
	//ENABLE_ADC_INT;
	AP_ADCC->INT_MASK = mAdc_Ctx.all_channel;

	//disableSleep();
	return SUCCESS;
}

/**
 * @fn      HalAdcChannelConfig
 * @brief   adc channel config
 * @param   clk - clock
 * @return  none
 */
int HalAdcChannelConfig(AdcCfg_t cfg, adc_Hdl_t evt_handler)
{
	uint8_t i;
	if(mAdc_Ctx.enable == FALSE)
	{
		return ERR_NOT_REGISTED;
	}

	if(evt_handler == NULL)
	{
		return ERR_INVALID_PARAM;
	}
	
	if((cfg.channel_flag & BIT(0)) || (cfg.channel_flag & BIT(1)))
	{
		return ERR_NOT_SUPPORTED;
	}

	ClearAdcConfig();

	AP_AON->PMCTL2_1 = 0x00;
	AP_PCRM->ANA_CTL &= ~BIT(0);
	AP_PCRM->ANA_CTL &= ~BIT(3);
	hal_clk_gate_disable(MOD_ADCC);
	hal_clk_reset(MOD_ADCC);
	
	mAdc_Ctx.continue_mode = cfg.is_continue_mode;
	mAdc_Ctx.all_channel = cfg.channel_flag & 0x03;
	for(i=2;i<8;i++){
		if(cfg.channel_flag & BIT(i)){
			if(i%2){
				mAdc_Ctx.all_channel |= BIT(i-1);
			}
			else{
				mAdc_Ctx.all_channel |= BIT(i+1);
			}
		}
	}
	mAdc_Ctx.chs_en_shadow = mAdc_Ctx.all_channel;
	if((AP_PCR->SW_CLK & BIT(MOD_ADCC)) == 0)
	{
		 hal_clk_gate_enable(MOD_ADCC);
	}

    //CLK_1P28M_ENABLE;
    AP_PCRM->CLKSEL |= BIT(6);
  
	//ENABLE_XTAL_OUTPUT;         //enable xtal 16M output,generate the 32M dll clock
    AP_PCRM->CLKHF_CTL0 |= BIT(18);
  
	//ENABLE_DLL;                  //enable DLL
    AP_PCRM->CLKHF_CTL1 |= BIT(7);
	
	//ADC_DBLE_CLOCK_DISABLE;      //disable double 32M clock,we are now use 32M clock,should enable bit<13>, diable bit<21>
    AP_PCRM->CLKHF_CTL1 &= ~BIT(21);//check
	 //subWriteReg(0x4000F044,21,20,3);  
	
	//ADC_CLOCK_ENABLE;            //adc clock enbale,always use clk_32M
    AP_PCRM->CLKHF_CTL1 |= BIT(13);

    //subWriteReg(0x4000f07c,4,4,1);    //set adc mode,1:mannual,0:auto mode
    AP_PCRM->ADC_CTL4 |= BIT(4);
//	  AP_PCRM->ADC_CTL4 |= BIT(3);
    AP_PCRM->ADC_CTL4 |= BIT(0);
//    AP_PCRM->ADC_CTL4 &= ~BIT(3); 								
    SetSamplingResolutionAuto(cfg.channel_flag, cfg.is_high_resolution);    	
	
	AP_PCRM->ADC_CTL0 &= ~BIT(20);
	AP_PCRM->ADC_CTL0 &= ~BIT(4);
	AP_PCRM->ADC_CTL1 &= ~BIT(20);
	AP_PCRM->ADC_CTL1 &= ~BIT(4);
	AP_PCRM->ADC_CTL2 &= ~BIT(20);
	AP_PCRM->ADC_CTL2 &= ~BIT(4);
	AP_PCRM->ADC_CTL3 &= ~BIT(20);
	AP_PCRM->ADC_CTL3 &= ~BIT(4);
	
	AP_PCRM->ANA_CTL &= ~BIT(23);//disable micbias
	AP_PCRM->ADC_CTL4 &= ~BIT(4); //enable auto mode 自动通道扫描方式
	mAdc_Ctx.evt_handler = evt_handler;
	for(i=MIN_ADC_CH;i<=MAX_ADC_CH;i++){
		if(cfg.channel_flag & BIT(i)){
			// GpioPin_t pin = s_pinmap[i];			
			// HalGpioPupdConfig(pin,GPIO_FLOATING);
			// HalGpioDsControl(pin, Bit_ENABLE);                				
			// HalGpioAnalogConfig(pin, Bit_ENABLE);
			
			switch (i)
			{
				case 0:
//						AP_PCRM->ADC_CTL0 |= BIT(20);
					break;
				case 1:
//						AP_PCRM->ADC_CTL0 |= BIT(4);
				 AP_ADCC->COMP_CFG[0] = (ADC_SAMPING_NUM<<24);
					break;
				case 2:
//						AP_PCRM->ADC_CTL1 |= BIT(20);
				  AP_ADCC->COMP_CFG[1] = (ADC_SAMPING_NUM<<24);
					break;
				case 3:
//						AP_PCRM->ADC_CTL1 |= BIT(4);
					AP_ADCC->COMP_CFG[2] = (ADC_SAMPING_NUM<<24);
					break; 
				case 4:
//						AP_PCRM->ADC_CTL2 |= BIT(20);
					AP_ADCC->COMP_CFG[3] = (ADC_SAMPING_NUM<<24);
					break;
				case 5:
//						AP_PCRM->ADC_CTL2 |= BIT(4);
					AP_ADCC->COMP_CFG[4] = (ADC_SAMPING_NUM<<24);
									
					break;
				case 6:									
//						AP_PCRM->ADC_CTL3 |= BIT(20);
					AP_ADCC->COMP_CFG[5] = (ADC_SAMPING_NUM<<24);
					break;
				case 7:
//						AP_PCRM->ADC_CTL3 |= BIT(4);
					AP_ADCC->COMP_CFG[6] = (ADC_SAMPING_NUM<<24);
					break;
				default:
					break;                    
			}		
		}		
	}
	
	
	return SUCCESS;
}

/**
 * @fn      HalAdcStop
 * @brief   stop
 * @param   none
 * @return  none
 */
int HalAdcStop(void)
{
    int i;
    uint8_t     all_channel2 = (((mAdc_Ctx.chs_en_shadow&0x80)>>1)|\
								((mAdc_Ctx.chs_en_shadow&0x40)<<1)|\
								((mAdc_Ctx.chs_en_shadow&0x20)>>1)|\
								((mAdc_Ctx.chs_en_shadow&0x10)<<1)|\
								((mAdc_Ctx.chs_en_shadow&0x08)>>1)|\
								((mAdc_Ctx.chs_en_shadow&0x04)<<1));
	
	if(mAdc_Ctx.enable == FALSE)
	{
        return ERR_NOT_REGISTED;
    }
    //MASK_ADC_INT;
    AP_ADCC->INT_MASK = 0x1ff;

    NVIC_DisableIRQ((IRQn_Type)ADCC_IRQn);
	JUMP_FUNCTION(ADCC_IRQ_HANDLER)                  =   0;
	
	AP_ADCC->INT_CLR = 0x1FF;
	
	//DISABLE_ADC;
    AP_PCRM->ANA_CTL &= ~BIT(3);
	
    //ADC_CLOCK_DISABLE;
//    if(g_system_clk != SYS_CLK_DBL_32M)
//	{
		AP_PCRM->CLKHF_CTL1 &= ~BIT(13);
//	}

    for(i =MIN_ADC_CH; i<= MAX_ADC_CH; i++){
        if(all_channel2 & BIT(i)){
            DisableAnalogPin((AdcChannel_t)i);
        }
    }	
	//  AP_PCRM->ANA_CTL &= ~BIT(0);//Power down analog LDO
	
	// hal_clk_reset(MOD_ADCC);
    // hal_clk_gate_disable(MOD_ADCC);
	
    ClearAdcConfig();
    //enableSleep();
    
    hal_pwrmgr_unlock(MOD_ADCC);
    return SUCCESS;
}




const unsigned int adc_Lambda[MAX_ADC_CH - MIN_ADC_CH + 1] =
{

    4519,//P11
    4308,//P23
    4263,//P24
    4482,//P14
    4180,//P15
    4072,//P20

};



/**
 * @fn      HalAdcValueCal
 * @brief   This function calibration ADC results
 * @param   
 * @return  none
 */
int32_t HalAdcValueCal(AdcChannel_t ch,uint16_t* buf, uint32_t size, uint8_t high_resol)
{
	uint32_t i;
	int adc_sum = 0;
	volatile int32_t result = 0;
	uint16_t adc_cal_postive = mAdc_Ctx.adc_cal_postive;
    uint16_t adc_cal_negtive = mAdc_Ctx.adc_cal_negtive;
	if(ch< MIN_ADC_CH || ch > MAX_ADC_CH)
	{
		return ERR_INVALID_PARAM;
	}
	
    for (i = 0; i < size; i++) {
       adc_sum += (buf[i]&0xfff);			
    }
  
    HalAdcLoadCalibrationValue();  
    result = adc_sum/size;
    if((adc_cal_postive!=0xfff)&&(adc_cal_negtive!=0xfff)){
        int delta = ((int)(adc_cal_postive-adc_cal_negtive))>>1;
        if(ch&0x01)
        {
            result =  ((result-delta)*1000 /(adc_cal_postive+adc_cal_negtive));
        }
        else
        {
            result =  ((result+delta)*1000 /(adc_cal_postive+adc_cal_negtive));
        }
    }
    else
    {
		
        result =  ((result*1000) >>12);
    }

    if(high_resol == TRUE)
    {
        result = result*8/10;	
    }
    else
    {
        result = result*adc_Lambda[ch-2]*8/10000;
    }

    return result;
}
